package com.pcitc.paas.feign.demo.api.processor;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.github.pagehelper.Page;
import feign.QueryMapEncoder;
import feign.codec.EncodeException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.ClassUtils;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * description: <br>
 * createTime: 2021/6/21 17:46 <br>
 *
 * @author zzh
 */
public class RequestObjectQueryMapEncoder implements QueryMapEncoder {
    private final ConcurrentHashMap<Class<?>, List<Field>> fieldMap = new ConcurrentHashMap();
    private final DateTimeFormatter LOCAL_DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    private final DateTimeFormatter LOCAL_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private static final String[] PRESENT_FIELD_NAME = new String[]{"pageSize", "pageIndex"};
    private static boolean JACKSON_PRESENT;

    public RequestObjectQueryMapEncoder() {
    }

    public Map<String, Object> encode(Object object) {
        if (ClassUtils.isPrimitiveOrWrapper(object.getClass())) {
            throw new EncodeException("@ParamObject can't be primitive or wrapper type");
        } else {
            Class<?> clazz = object.getClass();
            List<Field> fieldList = this.fieldMap.computeIfAbsent(clazz, this::fieldList);
            HashMap map = new HashMap(fieldList.size());

            try {
                Iterator var5 = fieldList.iterator();

                while(var5.hasNext()) {
                    Field field = (Field)var5.next();
                    Object fieldObj = field.get(object);
                    if (fieldObj != null) {
                        Class<?> fieldClazz = field.getType();
                        String name = "";
                        if (JACKSON_PRESENT && field.getDeclaredAnnotation(JsonProperty.class) != null) {
                            name = ((JsonProperty)field.getDeclaredAnnotation(JsonProperty.class)).value();
                        }

                        String localDateTime;
                        if (LocalDate.class.isAssignableFrom(fieldClazz)) {
                            localDateTime = this.LOCAL_DATE_FORMATTER.format((LocalDate)fieldObj);
                            map.put(name, localDateTime);
                        } else if (LocalDateTime.class.isAssignableFrom(fieldClazz)) {
                            localDateTime = this.LOCAL_DATE_TIME_FORMATTER.format((LocalDateTime)fieldObj);
                            map.put(name, localDateTime);
                        } else if (ClassUtils.isPrimitiveWrapperArray(fieldClazz)) {
                            map.put(name, StringUtils.join((Object[])((Object[])fieldObj), ","));
                        } else if (String[].class.isAssignableFrom(fieldClazz)) {
                            map.put(name, StringUtils.join((String[])((String[])fieldObj), ","));
                        } else {
                            map.put(name, fieldObj);
                        }
                    }
                }

                return map;
            } catch (IllegalAccessException var11) {
                throw new EncodeException("Fail encode ParamObject into query Map", var11);
            }
        }
    }

    private List<Field> fieldList(Class<?> clazz) {
        List<Field> fields = new ArrayList();
        Field[] var3 = clazz.getDeclaredFields();
        int var4 = var3.length;

        int var5;
        for(var5 = 0; var5 < var4; ++var5) {
            Field field = var3[var5];
            if (this.illegalField(field)) {
                fields.add(field);
            }
        }

        Class[] var8 = this.getAllSuperclasses(clazz);
        var4 = var8.length;

        for(var5 = 0; var5 < var4; ++var5) {
            Class<?> superClazz = var8[var5];
            if (!Object.class.equals(superClazz)) {
                boolean isPage = superClazz.equals(Page.class);
                Arrays.stream(superClazz.getDeclaredFields()).filter((fieldx) -> {
                    return !isPage || isPage && Arrays.stream(PRESENT_FIELD_NAME).anyMatch((s) -> {
                        return s.equalsIgnoreCase(fieldx.getName());
                    });
                }).forEach((fieldx) -> {
                    if (this.illegalField(fieldx)) {
                        fields.add(fieldx);
                    }

                });
            }
        }

        return fields;
    }

    private Class<?>[] getAllSuperclasses(Class<?> clazz) {
        List<Class<?>> result = new ArrayList();
        Class superclass = clazz.getSuperclass();

        do {
            result.add(superclass);
        } while(superclass == Object.class);

        return (Class[])result.toArray(new Class[0]);
    }

    private boolean illegalField(Field field) {
        Class<?> fieldType = field.getType();
        if (!ClassUtils.isPrimitiveOrWrapper(fieldType) && !String.class.isAssignableFrom(fieldType) && !String[].class.isAssignableFrom(fieldType) && !LocalDateTime.class.isAssignableFrom(fieldType) && !LocalDate.class.isAssignableFrom(fieldType) && !BigDecimal.class.isAssignableFrom(fieldType) && !BigInteger.class.isAssignableFrom(fieldType)) {
            return false;
        } else {
            if (!field.isAccessible()) {
                field.setAccessible(true);
            }

            return true;
        }
    }

    static {
        try {
            Class.forName("com.fasterxml.jackson.annotation.JsonProperty");
            JACKSON_PRESENT = true;
        } catch (ClassNotFoundException var1) {
            JACKSON_PRESENT = false;
        }

    }
}
